#include "cledMatrixRGB.h"
#include "cledMatrix.h"
#include "UGUI/ugui.h"
#include "LCD/lcd.h"
#include "cledMatrixRGB.h"
#include "Curve/hsv.h"
#include <math.h>

#define RGBGetRValue(rgb)   (rgb & 0x000000FF)
#define RGBGetGValue(rgb)   ((rgb >> 8) & 0x000000FF)
#define RGBGetBValue(rgb)   ((rgb >> 16) & 0x000000FF)

#define ToRGBColor(r, g, b) ((uint32_t)((b << 16) | (g << 8) | (r)))
static unsigned char ScreenChanel(unsigned char a, unsigned char b)
{
    return 255 - ((255 - b) * (255 - a) >> 8);;
}
static uint32_t Screen(uint32_t color1, uint32_t color2)
{
    return ToRGBColor(
                ScreenChanel(RGBGetRValue(color1), RGBGetRValue(color2)),
                ScreenChanel(RGBGetGValue(color1), RGBGetGValue(color2)),
                ScreenChanel(RGBGetBValue(color1), RGBGetBValue(color2))
                );
};
static unsigned char InterpolateChanel(unsigned char a, unsigned char b, float x)
{
    return (int) ((b - a) * x + a);
}
static uint32_t Interpolate(uint32_t color1, uint32_t color2, float fraction)
{
    return ToRGBColor(
                InterpolateChanel(RGBGetRValue(color1), RGBGetRValue(color2), fraction),
                InterpolateChanel(RGBGetGValue(color1), RGBGetGValue(color2), fraction),
                InterpolateChanel(RGBGetBValue(color1), RGBGetBValue(color2), fraction)
                );
}

/*************************************************************************************************\
|        Beams [9]                                                                              |
\*************************************************************************************************/
static hsv_t hsv1;
static hsv_t hsv2;
static double sine_x = 0;
static double sine_y = 0;
static int v_speed = 10;
static int h_speed = 15;
static int glow = 30;
static int thickness = 1;
static void CrossingBeams_SetRandomColorsEnabled(int RandomColorsEnabled)
{
    //RandomColorsEnabled = value;

//    if(!RandomColorsEnabled)
//    {
//        //ResetUserColors();
//        rgb2hsv(UserColors[0], &hsv1);
//        rgb2hsv(UserColors[1], &hsv2);
//    }
//    else
    {
        hsv1.hue = 0;
        hsv1.value = 255;
        hsv1.saturation = 255;

        hsv2.hue = 180;
        hsv2.value = 255;
        hsv2.saturation = 255;
    }
}
void cledMatrixCrossingBeams_Init(void)    // 交叉梁   // Fail
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 15;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    CrossingBeams_SetRandomColorsEnabled(1);
}
static uint32_t CrossingBeams_GetColor(unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{

    float x_progress = 0.5 * (1 + sine_x) * w;
    float y_progress = 0.5 * (1 + sine_y) * h;

    float distance_x = fabs(x_progress - x);
    //float distance_x_percent = std::min<float>(1,pow(distance_x / w,
    //                                                 distance_x > thickness ? 0.01 * glow : 1));
    float distance_x_percent = pow(distance_x / w, distance_x > thickness ? 0.01 * glow : 1);
    if(distance_x_percent>1) distance_x_percent=1;

    float distance_y = fabs(y_progress - y);
    //float distance_y_percent = std::min<float>(1, pow(distance_y / h,
    //                                                  distance_y > thickness ? 0.01 * glow : 1));
    float distance_y_percent = pow(distance_y / h, distance_y > thickness ? 0.01 * glow : 1);
    if(distance_y_percent>1) distance_y_percent=1;

    hsv_t hsv_tmp;

    hsv_tmp.value = hsv1.value * (1 - distance_x_percent);
    hsv_tmp.hue = hsv1.hue;
    hsv_tmp.saturation = hsv1.saturation;

    uint32_t color1 = (hsv2rgb(&hsv_tmp));

    hsv_tmp.value = hsv2.value * (1 - distance_y_percent);
    hsv_tmp.hue = hsv2.hue;
    hsv_tmp.saturation = hsv2.saturation;

    uint32_t color2 = (hsv2rgb(&hsv_tmp));

    return Screen(color1,color2);
}
void cledMatrixCrossingBeams(const uint32_t cols, const uint32_t rows)    // 交叉梁   // Fail
{
    sine_x = sin( 0.01 * h_speed * mParams.progress);
    sine_y = sin( 0.01 * v_speed * mParams.progress);
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = CrossingBeams_GetColor(col_id, row_id, cols, rows);
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress += (float) mParams.Speed / (float) mParams.FPS;

    //if(RandomColorsEnabled)
    {
        hsv1.hue++;
        hsv2.hue++;
    }
}

/*************************************************************************************************\
|        RotatingBeam [9]                                                                              |
\*************************************************************************************************/
struct Line{
    struct{
        float x,y;
    }p1,p2;
};
enum RotatingBeamMode
{
    CLOCKWISE = 0,
    COUNTER_CLOCKWISE = 1,
    PENDULUM = 2,
    WIPERS = 3,
    SWING_H = 4,
    SWING_V = 5
};
static enum RotatingBeamMode mode = CLOCKWISE;//CLOCKWISE;
static void RotatingBeamRemodel_SetRandomColorsEnabled(int RandomColorsEnabled)
{
//    RandomColorsEnabled = value;

//    if(!RandomColorsEnabled)
//    {
//        ResetUserColors();
//    }
//    else
    {
        hsv1.hue = 0;
        hsv1.value = 255;
        hsv1.saturation = 255;

        hsv2.hue = 180;
        hsv2.value = 255;
        hsv2.saturation = 255;
    }
}
void cledMatrixRotatingBeam_Init(void)    // 旋转射束  // 有效果,但不稳定
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 10;
    mParams.shape = 0;
    mParams.progress = 0.f;
    thickness = 0;
    //SetSlider2Val(10);
    RotatingBeamRemodel_SetRandomColorsEnabled(1);
}
static void RotatingBeamRemodel_Rotate(struct Line* const line)
{
    //struct Line line;

    switch (mode)
    {
    case CLOCKWISE:
    {
        float x = 0.5 * (1 + cos(mParams.progress));
        float y = 0.5 * (1 + sin(mParams.progress));

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(1 - x);
        line->p2.y=(1 - y);

        break;

    }
    case COUNTER_CLOCKWISE:
    {
        float x = 0.5 * (1 + cos(-mParams.progress));
        float y = 0.5 * (1 + sin(-mParams.progress));

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(1 - x);
        line->p2.y=(1 - y);

        break;

    }
    case PENDULUM:
    {
        float x = 0.5 * (1 + cos(mParams.progress));

        line->p1.x=(0.5);
        line->p1.y=(0);
        line->p2.x=(x);
        line->p2.y=(1);

        break;

    }
    case WIPERS:
    {
        float x = 0.5 * (1 + cos(mParams.progress));

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(0.5);
        line->p2.y=(1);

        break;

    }
    case SWING_H:
    {
        float x = 0.5 * (1 + cos(mParams.progress));

        line->p1.x=(0);
        line->p1.y=(x);
        line->p2.x=(1);
        line->p2.y=(1 - x);

        break;

    }
    case SWING_V:
    //default:
    {
        float x = 0.5 * (1 + cos(mParams.progress));

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(1 - x);
        line->p2.y=(1);

        break;

    }
    default: ;//return QLineF({0, 0}, {1, 1});
        line->p1.x=0;
        line->p1.y=0;
        line->p2.x=1;
        line->p2.y=1;
    }

    //return QLineF(p1, p2);
}
static uint32_t RotatingBeamRemodel_GetColor(float x0, float y0, struct Line* const l, float w, float h)
{
    float glow = 0.01 * mParams.Slider2Val;

    float x1 = l->p1.x * w;
    float x2 = l->p2.x * w;

    float y1 = l->p1.y * h;
    float y2 = l->p2.y * h;

    float distance = fabs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / sqrt(pow(x2 - x1 ,2) + pow(y2 - y1,2));

    hsv_t hsv_tmp;

    hsv_tmp.value = hsv1.value - hsv1.value * pow(distance / (0.5*(w+h)) , distance < thickness ? 1 : glow);
    hsv_tmp.hue = hsv1.hue;
    hsv_tmp.saturation = hsv1.saturation;

    uint32_t color1 = (hsv2rgb(&hsv_tmp));

    uint32_t color2 = (hsv2rgb(&hsv2));

    return Interpolate(color2, color1, 1 - distance / (0.5*(w+h)) );
}
void cledMatrixRotatingBeam(const uint32_t cols, const uint32_t rows)    // 旋转射束  // 有效果,但不稳定
{
    struct Line line; RotatingBeamRemodel_Rotate(&line);
    unsigned int width = cols;
    unsigned int height = rows;
    //unsigned int * map = controller_zone->map();

    for(unsigned int row_id = 0; row_id < height; row_id++)
    {
        for(unsigned int col_id = 0; col_id <  width; col_id++)
        {
            uint32_t color = RotatingBeamRemodel_GetColor(col_id, row_id, &line, width - 1, height - 1);

            //unsigned int led_num = map[h * width + w];
            //unsigned int led_num = h * width + w;
            //controller_zone->SetLED(start_idx + led_num,color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress +=  0.1 * (float) mParams.Speed / (float) mParams.FPS;

    //if(RandomColorsEnabled)
    {
        hsv1.hue++;
        hsv2.hue++;
    }
}
